// Copyright  2011, 2012 Petr TALLA. [petr.talla@gmail.com]
// This file is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY;  without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
// [http://www.gnu.org/copyleft/lesser.html]
//=============================================================================
#include "./TcCmdEngine.h"
#include "./TcArgCol.h"
#include "./TcArgVal.h"
#include "./TcCmd.h"
#include "./TcCmdProcess.h"
#include "./TcCmdProcessExe.h"
#include "./TcCmdEngineMsgReceiver.h"
#include <TcLock.h> //Temporary here
#include <cstring>
#include <cstdlib>

using namespace std;

//=============================================================================
TcCmdEngine::TcCmdEngine() :
    frontControllerActive_(false),
    loop_(0),
    pendingExecution_(false)
{   append(new TcCmdProcessExe(this));
}

//=============================================================================
TcCmd* TcCmdEngine::registerCmd(const char* name, TcCmd::Cmd cmd)
{   TcCmd* result = new TcCmd(name, cmd);
    items_.insert(pair<string, TcCmd*>(name, result));
    return result;
}

//=============================================================================
TcCmd* TcCmdEngine::find(TcArgCol& args)
{   if (args.count() == 0) return NULL;
    TcArgVal* name = args.at(0)->getAsVal();
    if ( name == NULL ) return NULL;
    map<string, TcCmd*>::iterator it = items_.find(name->value());
    if ( it == items_.end() ) return NULL;
    return it->second;
}

//=============================================================================
int TcCmdEngine::execute(TcArgCol* args, bool queued  )
{
    if (queued && queued_)
    {   queue_.push_back(args);
        return 0;
    }

    int result = 0;

#ifndef NDEBUG
    args->appendVal(loop_, "__SYS__LOOP");
#endif

    bool isFrontController = false;
    TcCmd* cmd = find(*args);
    if ( (cmd != NULL) && cmd->frontControll() && (frontControllerActive_ == false) )
    {   isFrontController = true;
    }
    if ( isFrontController ) frontControllerActive_ = true;

    list<TcCmdProcess*>::iterator it;
    for ( it = processes_.begin(); it != processes_.end(); it++ )
    {   TcCmdProcess* process = *it;
        result += process->execute(*args, isFrontController);
    }

    if ( isFrontController ) frontControllerActive_ = false;

    pendingExecution_ = true;

    return result;
}

//=============================================================================
void TcCmdEngine::executeQueue(void)
{   if ( !pendingExecution_ && queue_.empty() ) return;

    TcLock LOCK;
    loop_++;

    while (queue_.empty() == false)
    {
        TcArgCol* args = queue_.front();

#ifndef NDEBUG
        args->appendVal("", "__SYS__QUEUED");
#endif
        execute(args, false);

        queue_.pop_front();
    }

    list<TcCmdEngineMsgReceiver*>::iterator it;
    for ( it = msgReceivers_.begin(); it != msgReceivers_.end(); it++ )
    {   (*it)->onEmptyQueue();
    }

    pendingExecution_ = false;
}

//=============================================================================
TcCmdEngine& TcCmdEngine::engine()
{   static TcCmdEngine* theEngine = NULL;
    if ( theEngine == NULL ) theEngine = new TcCmdEngine();
    return *theEngine;
}

//=============================================================================
void TcCmdEngine::append(TcCmdProcess* process)
{   processes_.push_back(process);
}

//=============================================================================

